<?php

namespace app\modules\api\controllers;

use app\models\Agent;
use app\models\AgentPublish;
use app\models\ApiException;
use app\models\Card;
use app\models\CardAttr;
use app\models\CardAttrValue;
use app\models\CardReplace;
use app\models\SubUser;
use app\models\User;
use app\models\UserCardRecharge;
use app\models\Util;
use yii\data\Pagination;

/**
 * 卡接口
 * Class CardController
 * @package app\modules\api\controllers
 */
class CardController extends BaseController
{
    /**
     * 依次尝试根据id ICCID MSISDN IMSI获取卡
     * @param Agent|User|SubUser $user 登录代理商或用户
     * @param string $jsonPrefix JSON数据路径前缀
     * @return Card
     * @throws ApiException
     */
    private function getCard($user = null, $jsonPrefix = '')
    {
        $id = $this->json($jsonPrefix . 'id');
        $iccid = $this->json($jsonPrefix . 'iccid');
        $msisdn = $this->json($jsonPrefix . 'msisdn');
        $imsi = $this->json($jsonPrefix . 'imsi');

        $card = null;
        if (!empty($id)) {
            $card = Card::findOne(['id' => $id]);
        } elseif (!empty($iccid)) {
            $card = Card::findOne(['iccid' => $iccid]);
        } elseif (!empty($msisdn)) {
            $card = Card::findOne(['msisdn' => $msisdn]);
        } elseif (!empty($imsi)) {
            $card = Card::findOne(['imsi' => $imsi]);
        }

        if (!empty($card)) {
            if (!empty($user)) {
                if (($user instanceof Agent) && $card->agent_id != $user->id) {
                    throw new ApiException('NOT_FOUND', '没有找到卡信息。');
                }
                if (($user instanceof User) && $card->uid != $user->id) {
                    throw new ApiException('NOT_FOUND', '没有找到卡信息。');
                }
                if (($user instanceof SubUser) && $card->uid != $user->uid) {
                    throw new ApiException('NOT_FOUND', '没有找到卡信息。');
                }
            }
        }

        return $card;
    }

    /**
     * 卡列表
     * 输入
     * {
     *   search_attr: [
     *     {name, value},
     *     ...
     *   ]
     * }
     * 示例：
     * ```JSON
     * {
     *   "search_attr": [
     *     {"name": "车牌号", "value": "鲁A12345"},
     *     {"name": "电话", "value": "15600000000"}
     *   ]
     * }
     * ```
     * 输出
     * {
     *   card_list: [
     *     {id, iccid, msisdn, imsi, package_id, status, active_time, on_off},
     *     ...
     *   ],
     *   page: {}
     * }
     * @throws ApiException
     */
    public function actionList()
    {
        $user = $this->loginUser();
        $uid = ($user instanceof User) ? $user->id : $user->uid;

        $query = Card::find();
        $query->andWhere(['uid' => $uid]);
        if (!empty($this->json('search_attr'))) {
            foreach ($this->json('search_attr') as $item) {
                $attrQuery = CardAttr::find()
                    ->distinct()
                    ->select('id')
                    ->andWhere(['uid' => $uid])
                    ->andWhere(['status' => CardAttr::STATUS_OK])
                    ->andWhere(['name' => $item['name']]);
                $attrValueQuery = CardAttrValue::find()
                    ->distinct()
                    ->select('card_id')
                    ->andWhere(['uid' => $uid])
                    ->andWhere(['attr_id' => $attrQuery])
                    ->andWhere(['value' => $item['value']]);
                $query->andWhere(['id' => $attrValueQuery]);
            }
        }
        $pagination = new Pagination(['totalCount' => $query->count(), 'page' => $this->json('page', 1) - 1, 'validatePage' => false]);
        $query->orderBy('id DESC');
        $query->offset($pagination->offset)->limit($pagination->limit);
        $cardList = [];
        /** @var Card $card */
        foreach ($query->each() as $card) {
            $cardList[] = $card->getAttributes(['id', 'iccid', 'msisdn', 'imsi', 'package_id', 'status', 'active_time', 'on_off']);
        }
        return [
            'card_list' => $cardList,
            'page' => [
                'totalCount' => $pagination->totalCount,
                'pageCount' => $pagination->pageCount,
                'page' => $pagination->page + 1,
            ],
        ];
    }

    /**
     * 卡信息
     * 输入
     * {
     *     iccid,
     *     msisdn,
     *     imsi
     * }
     * 输出
     * {
     *     card: {
     *         id, api_id, iccid, msisdn, imsi, status,
     *         active_time, on_off, ip,
     *         data, sms, voice, refresh_time,
     *         term_length, term_length_unit, term_end_time
     *     },
     *     package: {
     *         id, name, data, sms, voice, price, extra_data, extra_data_price, extra_sms_price, extra_voice_price
     *     }
     * }
     * @throws ApiException
     */
    public function actionDetail()
    {
        $user = $this->loginUser();

        $card = $this->getCard($user);
        if (empty($card)) {
            throw new ApiException('NOT_FOUND', '没有找到卡信息。');
        }
        return [
            'card' => $card->getAttributes(['id', 'api_id', 'iccid', 'msisdn', 'imsi', 'status', 'active_time', 'on_off', 'ip', 'data', 'sms', 'voice', 'refresh_time', 'term_length', 'term_length_unit', 'term_end_time']),
            'package' => empty($card->package_id) ? null : array_merge(
                $card->package->getAttributes(['id', 'name', 'data', 'sms', 'voice', 'extra_data', 'extra_data_price', 'extra_sms_price', 'extra_voice_price']),
                [
                    'price' => $card->package->getRealPrice($card),
                ]
            ),
        ];
    }

    /**
     * 公开信息
     * 输入
     * {
     *     iccid,
     *     msisdn,
     *     imsi
     * }
     * 输出
     * {
     *     card: {
     *         id, api_id, iccid, msisdn, imsi, status,
     *         active_time, on_off, ip,
     *         data, sms, voice, refresh_time,
     *         term_length, term_length_unit, term_end_time
     *     },
     *     package: {
     *         id, name, data, sms, voice, price, extra_data, extra_data_price, extra_sms_price, extra_voice_price
     *     },
     *     publish_list: [
     *         {id, name, type, value},
     *         ...
     *     ]
     * }
     * @throws ApiException
     */
    public function actionPublicDetail()
    {
        $card = $this->getCard();
        if (empty($card)) {
            throw new ApiException('NOT_FOUND', '没有找到卡信息。');
        }
        $publishList = [];
        /** @var AgentPublish $publish */
        foreach (AgentPublish::find()
            ->andWhere(['agent_id' => $card->agent_id])
            ->orderBy('sort DESC')
            ->each() as $publish) {
            $value = $publish->value;
            if (in_array($publish->type, [AgentPublish::TYPE_FILE, AgentPublish::TYPE_IMAGE])) {
                $value = Util::fileUrl($value);
            }
            $publishList[] = array_merge(
                $publish->getAttributes(['id', 'name', 'type']),
                ['value' => $value]
            );
        }
        return [
            'card' => $card->getAttributes(['id', 'api_id', 'iccid', 'msisdn', 'imsi', 'status', 'active_time', 'on_off', 'ip', 'data', 'sms', 'voice', 'refresh_time', 'term_length', 'term_length_unit', 'term_end_time']),
            'package' => empty($card->package_id) ? null : array_merge(
                $card->package->getAttributes(['id', 'name', 'data', 'sms', 'voice', 'extra_data', 'extra_data_price', 'extra_sms_price', 'extra_voice_price']),
                [
                    'price' => $card->package->getRealPrice($card),
                ]
            ),
            'publish_list' => $publishList,
        ];
    }

    /**
     * 刷新卡
     * 输入
     * {
     *     iccid,
     *     msisdn,
     *     imsi
     * }
     * 输出
     * {}
     * @throws ApiException
     */
    public function actionRefresh()
    {
        $user = $this->loginUser();

        $card = $this->getCard($user);
        if (empty($card)) {
            throw new ApiException('NOT_FOUND', '没有找到卡信息。');
        }
        try {
            $card->refreshBasic();
            return [];
        } catch (\Exception $e) {
            throw new ApiException('SERVER', $e->getMessage());
        }
    }

    /**
     * 设置卡状态
     * 输入
     * {
     *     iccid,
     *     msisdn,
     *     imsi,
     *     status
     * }
     * 输出
     * {}
     * @throws ApiException
     */
    public function actionSetStatus()
    {
        $user = $this->loginUser();

        $card = $this->getCard($user);
        if (empty($card)) {
            throw new ApiException('NOT_FOUND', '没有找到卡信息。');
        }
        try {
            $card->setStatus($this->json('status'));
        } catch (\Exception $e) {
            throw new ApiException('SERVER', $e->getMessage());
        }
    }

    /**
     * 保存卡属性值
     * 输入
     * {
     *   card: {iccid},
     *   attr_list: [
     *     {name, value},
     *     ...
     *   ]
     * }
     * 输出
     * {}
     * @throws ApiException
     */
    public function actionSaveAttrValue()
    {
        $user = $this->loginUser('user');

        $this->checkJson([
            ['attr_list', 'required'],
        ]);

        $card = $this->getCard($user, 'card/');
        if (empty($card)) {
            throw new ApiException('NOT_FOUND', '没有找到卡信息。');
        }

        foreach ($this->json('attr_list') as $item) {
            $name = $item['name'];
            $value = $item['value'];

            $attr = CardAttr::findOne(['uid' => $card->uid, 'name' => $name]);
            if (empty($attr)) {
                throw new ApiException('NOT_FOUND', '没有找到属性【' . $name . '】。');
            }
            $attrValue = CardAttrValue::findOne(['uid' => $card->uid, 'card_id' => $card->id, 'attr_id' => $attr->id]);
            if (empty($attrValue)) {
                $attrValue = new CardAttrValue();
                $attrValue->uid = $card->uid;
                $attrValue->card_id = $card->id;
                $attrValue->attr_id = $attr->id;
            }
            $attrValue->value = $value;
            if (!$attrValue->save()) {
                throw new ApiException('SERVER', '无法保存属性值。', $attrValue->errors);
            }
        }
        $card->updateAttributes(['attr_update_time' => time()]);

        return [];
    }

    /**
     * 卡替换
     * 输入
     * {
     *   old: {
     *     iccid
     *   },
     *   new: {
     *     iccid
     *   }
     * }
     * @throws ApiException
     */
    public function actionReplace()
    {
        $user = $this->loginUser();

        if (!($user instanceof SubUser)) {
            throw new ApiException('FORBIDDEN', '此接口仅限子用户使用。');
        }

        $oldCard = $this->getCard($user, 'old/');
        $newCard = $this->getCard($user, 'new/');
        if (empty($oldCard)) {
            throw new ApiException('NOT_FOUND', '没有找到旧卡信息。');
        }
        if (empty($newCard)) {
            throw new ApiException('NOT_FOUND', '没有找到新卡信息。');
        }

        $replace = new CardReplace();
        $replace->from_card_id = $oldCard->id;
        $replace->to_card_id = $newCard->id;
        $replace->require_sub_uid = $user->id;
        $replace->status = CardReplace::STATUS_WAIT;
        $replace->create_time = time();
        if (!$replace->save()) {
            throw new ApiException('SERVER', '无法保存卡替换信息。', $replace->errors);
        }

        return [];
    }

    /**
     * 充值
     * 输入
     * {
     *   card: {iccid, msisdn, imsi},
     *   term_length,
     *   price, money,
     *   trade_type, is_paid,
     *   wx_app_id, wx_open_id,
     *   extra_info: {username, tel, message}
     * }
     * 输出
     * {}
     * @throws ApiException
     */
    public function actionRecharge()
    {
        $user = $this->loginUser();

        if (!($user instanceof User)) {
            throw new ApiException('FORBIDDEN', '此接口仅限终端用户使用。');
        }

        $this->checkJson([
            [['card', 'term_length', 'price', 'money', 'trade_type', 'is_paid'], 'required'],
        ]);

        $card = $this->getCard($user, 'card/');
        if (empty($card)) {
            throw new ApiException('NOT_FOUND', '没有找到卡信息。');
        }
        if (empty($card->package_id)) {
            throw new ApiException('CARD_PACKAGE', '没有设置卡套餐信息。');
        }
        if (empty($card->term_length_unit)) {
            throw new ApiException('CARD_TERM', '卡套餐时长单位没有设置。');
        }

        $isPaid = $this->json('is_paid') == 1;

        $recharge = new UserCardRecharge();
        $recharge->uid = $user->id;
        $recharge->card_id = $card->id;
        $recharge->term_length = $this->json('term_length');
        $recharge->term_length_unit = UserCardRecharge::TERM_LENGTH_UNIT_MONTH;
        $recharge->price = $this->json('price');
        $recharge->money = $this->json('money');
        $recharge->wx_app_id = $this->json('wx_app_id');
        $recharge->wx_open_id = $this->json('wx_open_id');
        $recharge->trade_type = $this->json('trade_type');
        $recharge->status = $isPaid ? UserCardRecharge::STATUS_PAID : UserCardRecharge::STATUS_WAIT_PAY;
        $recharge->create_time = time();
        if ($isPaid) {
            $recharge->pay_time = time();
        }
        $recharge->remark = json_encode($this->json('extra_info'));
        if (!$recharge->save()) {
            throw new ApiException('SERVER', '无法保存充值记录。', $recharge->errors);
        }

        return [];
    }
}
